﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Xml.Linq;
using Puzzler.Common;
using Puzzler.Common.Exceptions;

namespace Puzzler.Plugin.Interface
{
    public abstract class GridPuzzleIOHandler<TModel, TInfo, TUpgrate> : PuzzleIOHandler<TModel, TUpgrate> 
        where TModel : GridPuzzleModel<TInfo> 
        where TInfo : GridCellInfo
        where TUpgrate : GridPuzzleIOUpgrateHandler<TModel, TInfo>
    {
        protected GridPuzzleIOHandler(TModel model) : base(model)
        {
        }

        protected sealed override void SaveToXElement(XElement xElement)
        {
            XElement settings = new XElement("Settings");
            SaveSettingsToXElement(settings);

            XElement data = new XElement("Data");
            data.Add(new XAttribute("column-count", Model.GridColumnCount));
            data.Add(new XAttribute("row-count", Model.GridRowCount));

            for (int i = 0; i < Model.GridColumnCount; i++)
            {
                for (int j = 0; j < Model.GridRowCount; j++)
                {
                    XElement cell = new XElement("Cell");
                    cell.Add(new XAttribute("column", i));
                    cell.Add(new XAttribute("row", j));
                    TInfo info = Model[i, j];

                    if (info == null)
                    {
                        cell.Add(new XAttribute("is-null", true));
                    }
                    else
                    {
                        cell.Add(new XAttribute("is-null", false));
                        SaveCellInfoToXElemenmt(info, cell);
                    }

                    data.Add(cell);
                }
            }

            xElement.Add(settings);
            xElement.Add(data);
        }

        protected sealed override void LoadFromXElement(XElement element)
        {
            //Read settings
            XElement settings = element.ReadFromElement("Settings");
            LoadSettingsFromXElement(settings);

            //Read data
            XElement data = element.ReadFromElement("Data");
            int column = data.ReadFromAttribute("column-count", Int32.Parse);
            int row = data.ReadFromAttribute("row-count", Int32.Parse);

            IEnumerable<XElement> cells = data.Elements("Cell");
            if (cells == null || cells.Count() != column * row)
                throw new PuzzlerIOException("Grid Size and saved Grid Cell Count not match (Size: " + column + " x " +
                                             row + "; Cell Count: " + cells.Count());

            //Rebuild grid
            Model.Grid = new TInfo[column][];
            for (int i = 0; i < column; i++)
            {
                Model.Grid[i] = new TInfo[row];
            }

            //Read all cells
            foreach (XElement cell in cells)
            {
                int x = cell.ReadFromAttribute("column", Int32.Parse);
                int y = cell.ReadFromAttribute("row", Int32.Parse);
                bool isNull = cell.ReadFromAttribute("is-null", Boolean.Parse);

                if (isNull)
                {
                    Model.Grid[x][y] = null;
                }
                else
                {
                    TInfo info = Activator.CreateInstance<TInfo>();
                    LoadCellInfoFromXElement(info, cell);

                    Model.Grid[x][y] = info;
                }
            }
        }

        protected abstract void SaveSettingsToXElement(XElement xElement);
        protected abstract void LoadSettingsFromXElement(XElement xElement);

        protected abstract void SaveCellInfoToXElemenmt(TInfo info, XElement xElement);
        protected abstract void LoadCellInfoFromXElement(TInfo info, XElement xElement);
    }
}
